package cn.neo.hhrpc.core.registry.hh;

import cn.neo.hhrpc.core.consumer.HttpInvoker;
import cn.neo.hhrpc.core.meta.InstanceMeta;
import cn.neo.hhrpc.core.meta.ServiceMeta;
import cn.neo.hhrpc.core.registry.ChangedListener;
import cn.neo.hhrpc.core.registry.Event;
import cn.neo.hhrpc.core.registry.RegistryCenter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 实现HH注册中心
 */
@Slf4j
public class HHRegistryCenter implements RegistryCenter {

    private static final String REG_PATH = "/register";
    private static final String UNREG_PATH = "/unregister";
    private static final String FINDALL_PATH = "/getAllInstances";
    private static final String VERSION_PATH = "/version";
    private static final String RENEWS_PATH = "/renews";


    @Value("${hhregistry.servers}")
    private String servers;

    // 存储本地版本: 服务:版本
    Map<String, Long> VERSIONS = new HashMap<>();
    MultiValueMap<InstanceMeta, ServiceMeta> RENEWS = new LinkedMultiValueMap<>();

    HHHealthChecker healthChecker = new HHHealthChecker();


    @Override
    public void start() {
        log.info(" ===>>> [HHRegistry] : start with server : {}", servers);
        healthChecker.start();
        providerCheck();
    }

    @Override
    public void stop() {
        log.info(" ===>>> [HHRegistry] : stop with server : {}", servers);
        healthChecker.stop();
    }


    @Override
    public void register(ServiceMeta service, InstanceMeta instance) {
        log.info(" ===>>> [HHRegistry] : register instance {} for {}", instance, service);
        HttpInvoker.httpPost(JSON.toJSONString(instance), registerPath(service), InstanceMeta.class);
        log.info(" ===>>> [HHRegistry] : register {}", instance);
        RENEWS.add(instance, service);
    }

    @Override
    public void unregister(ServiceMeta service, InstanceMeta instance) {
        log.info(" ===>>> [HHRegistry] : unregister instance {} for {}", instance, service);
        HttpInvoker.httpPost(JSON.toJSONString(instance), unregisterPath(service), InstanceMeta.class);
        log.info(" ===>>> [HHRegistry] : unregister {}", instance);
        RENEWS.remove(instance, service);
    }


    @Override
    public List<InstanceMeta> fetchAll(ServiceMeta service) {
        log.info(" ===>>> [HHRegistry] : find all instance for {}", service);
        List<InstanceMeta> instances = HttpInvoker.httpGet(findAllPath(service), new TypeReference<List<InstanceMeta>>() {
        });
        log.info(" ===>>> [HHRegistry] : find all {}", instances);
        return instances;
    }


    @Override
    public void subscribe(ServiceMeta service, ChangedListener listener) {
        // 延迟1s执行，每5s执行一次
        healthChecker.consumerCheck(() -> {
            // 获取当前版本
            Long currentVersion = VERSIONS.getOrDefault(service.toPath(), -1L);
            // 获取配置中心版本
            Long newVersion = HttpInvoker.httpGet(versionPath(service), Long.class);
            log.info(" ===>>> [HHRegistry] : currentVersion = {}, newVersion = {}", currentVersion, newVersion);

            if (newVersion > currentVersion) {
                // 版本有更新，获取配置中心最新数据
                List<InstanceMeta> instances = fetchAll(service);
                // 通知监听器
                listener.fire(new Event(instances));
                // 更新最新版本
                VERSIONS.put(service.toPath(), newVersion);
            }

        });
    }

    private void providerCheck() {
        healthChecker.providerCheck(() -> {
            RENEWS.keySet().stream().forEach(instance -> {
//                String services = String.join(",", RENEWS.get(instance).stream().map(ServiceMeta::toPath).toArray(String[]::new));
//                Long timestamp = HttpInvoker.httpPost(JSON.toJSONString(instance), servers + RENEWS_PATH +"?services=" + services, Long.class);
                Long timestamp = HttpInvoker.httpPost(JSON.toJSONString(instance),
                        renewsPath(RENEWS.get(instance)), Long.class);
                log.info(" ===>>> [HHRegistry] : renew instance {} at {}", instance, timestamp);

            });

        });
    }


    private String findAllPath(ServiceMeta service) {
        return path(FINDALL_PATH, service);

    }

    private String versionPath(ServiceMeta service) {
        return path(VERSION_PATH, service);

    }

    private String registerPath(ServiceMeta service) {
        return path(REG_PATH, service);

    }

    private String unregisterPath(ServiceMeta service) {
        return path(UNREG_PATH, service);
    }

    private String renewsPath(List<ServiceMeta> serviceList) {
        return path(RENEWS_PATH, serviceList);
    }


    private String path(String context, ServiceMeta service) {
        return servers + context + "?service=" + service.toPath();
    }

    private String path(String context, List<ServiceMeta> serviceList) {
        String services = String.join(",", serviceList.stream().map(ServiceMeta::toPath).toArray(String[]::new));
        log.info(" ===>>> [HHRegistry] : renew instance {}", serviceList);
        return servers + context + "?services=" + services;
    }
}
